spaceharderfandomcom-20200214-history
Tutorial 2: Understanding the Component Architecture
SpaceRTS uses a component-based architecture, and a scene graph. In this tutorial, I will explain how the component-based architecture works in the SpaceRTS engine, and give some examples how how this works in the current demo. Components A component-based architecture is a way of organizing a game engine based on something called a component, rather than on an entity. In entity-based game engines, an entity is an object in the game (like the player, or a unit, or a missile), and is the smallest structure in it. Entities have data, and methods for operating on the data, and are defined by inheritance. For instance, in an entity based game, you might have an entity called Unit, which is itself a kind of entity called a Sprite. A Unit entity might have sub-classes like Fighter and Carrier, each which have their own special methods. By contrast, in a component-based architecture, the smallest structure is called a component. A component is a self-contained system that an entity uses to perform a particular task. An entity in a component-based system is simply a collection of components, rather than a thing in itself. For instance, there might be a Turret component, and a Sprite component, and an AI component, and a ship would just be a collection of these components. This kind of architecture has many advantages, and is especially good for RTS games. Using a component-based architecture, you can create new kinds of entities very easily, and can modify entities at run time. For instance, in a component-based architecture, you could have each component be destructable, so that ships shooting at one another could explode each other's parts. Doing this in an entity-based system would be much more dificult. Scene Graph Components in SpaceRTS are arranged in what's called a scene graph. This means that every component in the game has a position and orientation defined by its relationship to its parent component. That is, every component only knows how far away it is from its parent, and what its rotation is relative to the parent. An example of this is the way the components of a ship are arranged. Let's look at the components of the Enforcer Frigate: *'PhysBody' Position (0,0). Orientation (0) *'Sprite '''Position (0,0). Orientation (0) *'Machine Gun''' Position (-0.1, 0). Orientation (3.14159) *'Cannon 1' Position (0.1, 0). Orientation (0) *'Cannon 2' Position (0.2, 0). Orientation (0) *'Blinky Light 1' Position (0.1, 0.5). Orientation (0) *'Blinky Light 2 P'osition (0.1, -0.5). Orientation (0) *'Thruster 1 '''Position (-0.2, 0.1). Orientation (3.14159) *'Thruster 2''' Position (-0.2, -0.1). Orientation (3.14159) *'AI' Position (0,0). Orientation (0) *'Health' Position (0,0). Orientation (0) *'Special Effect' (sound) Position (0,0). Orientation (0) *'Special Effect '(death explosion) Position (0,0). Orientation (0) *'DeathSpawner '(for debris) Position (0,0). Orientation (0) Further, some of these components have sub-components. The guns, for instance, have sensor components and muzzle flash components. Each of these components is rigidly connected to the center of the object. The turrets are slightly forward, and the thrusters are slightly backwards. The sprite rotates around the center of the object. As the frigate moves and rotates around the world, all of its child components move and rotate with it. The scene graph allows us to perform operations on entire sub-trees of the world. For instance, if the Physbody of the ship is destroyed, then so are all the children of the physbody. Imagine a multi-segmented ship made of many parts. We could damage and destroy some parts of the ship without damaging others. Component Manager The components in the game are handled by a component manager which sits on top of them. The component manager keeps a root component (the start of the tree), and can perform operations on the components. To keep track of components, the component manager gives every component a unique ID (an unsigned long). In this way, it can access every component and handle operations over them. The component manager also keeps track of a few special components, such as the physics system, the sound effect manager, the level manager, and so on. These can be accessed from the component manager directly by other components that need them. Every component has a reference to the component manager that it lives in. Message Passing One of the disadvantages of component-based architectures is that it can be difficult to access other components within the same entity. For instance, if a physics body knows that it's been hit by a bullet, how does the physics body then communicate this information to the health system? The answer is, the physics body passes a message up the tree to its parent, which then passes the message down to the health system. There is a general "Message" class for passing information between components. By default, this is handled by the "RecieveMessage" function, and the message is passed down to all of the children of the component. If you want to send a message or register an event with a component, you should extend the Message class to make your own kind of message. For instance, the DamageMessage class is a special kind of message which tells Health components to take damage. Once you've created your own kind of message, you should handle that message in your component's ReceiveMessage function. XML Serialization One more advantage of component based architectures is that all entities can be defined by their data, and so can be created at run-time. That means, we can put all the data related to an entity in a data file, and load it later. In SpaceRTS, each and every component in the game can be saved and loaded to and from XML files. XML is a very simple markup language specifying fields and values. In Visual C#, all the public fields of a class can be written to and loaded from XML files. In this way, units can be easily created for SpaceRTS without anyone ever touching the code. All of the units in SpaceRTS are stored in their own XML files. Eventually, individual components could also be stored. In order to make your components serializable to XML, you must add them to the giant list of serializable classes in '''XmlSerialization.cs '''after that, including a reference to your component in an XML file will cause that component to be included in the entity when it is spawned. Category:Tutorials